package com.hiss.basic.system.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.hiss.basic.client.system.ISysMenuService;
import com.hiss.basic.client.system.ISysRoleMenuService;
import com.hiss.basic.client.system.ISysUserRoleService;
import com.hiss.basic.model.system.domain.SysMenuDomain;
import com.hiss.basic.model.system.domain.SysRoleDomain;
import com.hiss.basic.model.system.domain.SysRoleMenuDomain;
import com.hiss.basic.model.system.enums.MenuTypeEnum;
import com.hiss.basic.model.system.request.SysMenuCreateRequest;
import com.hiss.basic.model.system.request.SysMenuQueryRequest;
import com.hiss.basic.model.system.request.SysMenuUpdateRequest;
import com.hiss.basic.model.system.response.SysMenuQueryResponse;
import com.hiss.basic.model.system.response.SysMenuQueryTreeResponse;
import com.hiss.basic.model.system.response.route.RouteResponse;
import com.hiss.basic.starter.core.constant.SystemConstant;
import com.hiss.basic.starter.core.enums.EnableStatusEnum;
import com.hiss.basic.starter.core.exception.BusinessException;
import com.hiss.basic.starter.core.helper.MapstructHelper;
import com.hiss.basic.starter.core.utils.tree.TreeBuilder;
import com.hiss.basic.starter.core.utils.tree.TreeNode;
import com.hiss.basic.starter.security.helper.SecurityHelper;
import com.hiss.basic.system.mapper.SysMenuMapper;
import com.hiss.basic.system.utlis.RouteUtils;
import com.mybatisflex.core.query.QueryChain;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 系统菜单 service impl
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenuDomain> implements ISysMenuService {

    @Resource
    private ISysUserRoleService sysUserRoleService;

    @Resource
    private ISysRoleMenuService sysRoleMenuService;

    @Override
    public List<RouteResponse> findRoutes(Long userId) {
        List<SysMenuQueryTreeResponse> menuList = ListUtil.toList();

        if (SecurityHelper.isSuperAdmin()) {
            menuList = QueryChain.of(SysMenuDomain.class)
                    .in(SysMenuDomain::getMenuType, MenuTypeEnum.DIRECTORY.getCode(), MenuTypeEnum.MENU.getCode())
                    .eq(SysMenuDomain::getEnableStatus, EnableStatusEnum.ENABLE.getCode())
                    .listAs(SysMenuQueryTreeResponse.class);
        } else {
            Collection<Long> userRoleIdList = sysUserRoleService.findRoleIdListByUserId(userId);
            if (CollectionUtil.isNotEmpty(userRoleIdList)) {
                List<SysRoleDomain> roleList = QueryChain.of(SysRoleDomain.class)
                        .in(SysRoleDomain::getId, userRoleIdList)
                        .eq(SysRoleDomain::getEnableStatus, EnableStatusEnum.ENABLE.getCode())
                        .list();

                if (CollectionUtil.isNotEmpty(roleList)) {
                    userRoleIdList = roleList.stream().map(SysRoleDomain::getId).collect(Collectors.toSet());

                    List<SysRoleMenuDomain> roleMenuList = sysRoleMenuService.findList(userRoleIdList);
                    if (CollectionUtil.isNotEmpty(roleMenuList)) {
                        Set<Long> menuIdList = roleMenuList.stream().map(SysRoleMenuDomain::getMenuId).collect(Collectors.toSet());

                        menuList = QueryChain.of(SysMenuDomain.class)
                                .in(SysMenuDomain::getId, menuIdList)
                                .in(SysMenuDomain::getMenuType, MenuTypeEnum.DIRECTORY.getCode(), MenuTypeEnum.MENU.getCode())
                                .eq(SysMenuDomain::getEnableStatus, EnableStatusEnum.ENABLE.getCode())
                                .listAs(SysMenuQueryTreeResponse.class);
                    }
                }
            }
        }

        if (CollectionUtil.isNotEmpty(menuList)) {
            List<TreeNode<Long, Integer>> domainList = ListUtil.toList();
            domainList.addAll(menuList);

            List<TreeNode<Long, Integer>> menuTree = TreeBuilder.build(domainList,
                    parentId -> ObjectUtil.equal(SystemConstant.ROOT_PARENT_ID, parentId),
                    Comparator.comparingInt(TreeNode::getWeight));

            return RouteUtils.buildRouteTree(menuTree);
        }

        return ListUtil.toList();
    }

    @Override
    public List<SysMenuQueryResponse> findList(SysMenuQueryRequest request) {
        QueryWrapper wrapper = new QueryWrapper();
        if (ObjectUtil.isNotNull(request)) {
            wrapper.like(SysMenuDomain::getTitle, request.getTitle(), StrUtil.isNotBlank(request.getTitle()));
            wrapper.eq(SysMenuDomain::getEnableStatus, request.getEnableStatus(), ObjectUtil.isNotNull(request.getEnableStatus()));
        }
        wrapper.orderBy(SysMenuDomain::getCreateTime, false);
        return listAs(wrapper, SysMenuQueryResponse.class);
    }

    @Override
    public List<TreeNode<Long, Integer>> findTreeList(SysMenuQueryRequest request) {
        QueryWrapper wrapper = new QueryWrapper();
        if (ObjectUtil.isNotNull(request)) {
            wrapper.like(SysMenuDomain::getTitle, request.getTitle(), StrUtil.isNotBlank(request.getTitle()));
            wrapper.eq(SysMenuDomain::getEnableStatus, request.getEnableStatus(), ObjectUtil.isNotNull(request.getEnableStatus()));
        }

        List<SysMenuQueryTreeResponse> menuList = listAs(wrapper, SysMenuQueryTreeResponse.class);

        List<TreeNode<Long, Integer>> domainList = ListUtil.toList();
        domainList.addAll(menuList);

        return TreeBuilder.build(domainList, parentId -> ObjectUtil.equal(SystemConstant.ROOT_PARENT_ID, parentId),
                Comparator.comparingInt(TreeNode::getWeight));
    }

    @Override
    public void create(SysMenuCreateRequest request) {
        SysMenuCreateRequest.validate(request);

        SysMenuDomain domain = MapstructHelper.convert(request, SysMenuDomain.class);
        if (ObjectUtil.isNull(domain.getParentId())) {
            domain.setParentId(SystemConstant.ROOT_PARENT_ID);
        }
        save(domain);
    }

    @Override
    public void update(SysMenuUpdateRequest request) {
        SysMenuUpdateRequest.validate(request);

        SysMenuDomain domain = MapstructHelper.convert(request, SysMenuDomain.class);
        if (ObjectUtil.isNull(domain.getParentId())) {
            domain.setParentId(SystemConstant.ROOT_PARENT_ID);
        }
        updateById(domain);
    }

    @Override
    public void remove(Long id) {
        long count = QueryChain.of(SysMenuDomain.class)
                .eq(SysMenuDomain::getParentId, id)
                .count();
        if (count > 0) {
            throw new BusinessException("存在子级菜单，无法删除");
        }
        count = QueryChain.of(SysRoleMenuDomain.class)
                .eq(SysRoleMenuDomain::getMenuId, id)
                .count();
        if (count > 0) {
            throw new BusinessException("存在角色关联，无法删除");
        }
        removeById(id);
    }
}
